package com.wanhe.soserver;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Keep;
import android.text.TextUtils;

import com.wanhe.protocollibrary.ProtocolConst;
import com.wanhe.soserver.util.NumberBytes;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * PAD平板本地socket服务端抽象
 * <p>
 * <p>
 * Created by cdj on 2017/3/14.
 */

public class PSServer {

    private ExecutorService mExecutorServicePool;

    @Keep
    public static final int ERROR_STATE_CONECTING = 0;
    @Keep
    public static final int ERROR_STATE_OFFLINE = 1;
    @Keep
    public static final int ERROR_STATE_INIT = 2;

    @Keep
    public static final int ERROR_MSG_RECEIVE = 3;
    @Keep
    public static final int ERROR_MSG_SEND = 4;


    private static final int RECEIVE_MSG = 5;
    private static final String RECEIVE_MSG_KEY = "msg_receive";
    private static final String RECEIVE_MSG_TYPE_KEY = "msg_receive_type";

    private static final String ERROR_MSG_KEY = "msg_err";


    private static PSServer ourInstance;
    private ServerConfig mServerConfig;
    private ExecutorService serverThread;
    private boolean isServerOnline;
    private ServerSocket serverSocket;

    private List<Socket> socketClients;

    @Keep
    public static PSServer getInstance() {
        if (ourInstance == null) {
            ourInstance = new PSServer();
        }
        return ourInstance;
    }

    private PSServer() {
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case ERROR_STATE_CONECTING:
                    case ERROR_STATE_OFFLINE:
                    case ERROR_STATE_INIT:
                        if (stateListener != null) {
                            stateListener.error(msg.what);
                        }
                        break;
                    case ERROR_MSG_RECEIVE:
                        if (msgChangeListener != null) {
                            msgChangeListener.error(msg.getData().getString(ERROR_MSG_KEY));
                        }
                        break;
                    case RECEIVE_MSG:
                        if (msgChangeListener != null) {
                            msgChangeListener.receiveClientMsg(msg.getData().getInt(RECEIVE_MSG_TYPE_KEY),
                                    msg.getData().getString(RECEIVE_MSG_KEY));
                        }
                        break;
                }
                super.handleMessage(msg);
            }
        };
    }

    @Keep
    public void init(ServerConfig config) {
        this.mServerConfig = config;
        this.socketClients = new ArrayList<>();
    }

    @Keep
    public void startSocketServer() {
        star();
    }

    private void star() {
        initSocketServer();
    }

    private void initSocketServer() {
        try {
            serverSocket = new ServerSocket(this.mServerConfig.getPort());
            serverThread = Executors.newSingleThreadExecutor();
            mExecutorServicePool = Executors.newCachedThreadPool();  //创建一个线程池
            this.isServerOnline = true;
            serverThread.execute(new ServiceRunable());
            if (stateListener != null) {
                stateListener.online();
            }
        } catch (IOException e) {
            mHandler.sendEmptyMessage(ERROR_STATE_INIT);
        }


        // TODO: 2017/3/14 发送协议报文时赋值密文
    }

    private class ServiceRunable implements Runnable {

        @Override
        public void run() {
            while (isServerOnline) {
                //服务端开始运行
                try {
                    Socket socketClient = serverSocket.accept();
                    socketClients.add(socketClient);
                    mExecutorServicePool.execute(new ClientRunble(socketClient));
                } catch (IOException e) {
                    mHandler.sendEmptyMessage(ERROR_STATE_CONECTING);
                }
            }
        }
    }

    private class ClientRunble implements Runnable {

        private final Socket mSocket;
        private final InputStream is;

        private boolean isConected = true;

        public ClientRunble(Socket socket) throws IOException {
            this.mSocket = socket;
            this.is = this.mSocket.getInputStream();
        }

        @Override
        public void run() {
            while (isConected) {
                try {
                    byte[] totolLength = new byte[4];
                    if (is.read(totolLength) != -1) {
                        //获取长度
                        int length = NumberBytes.bytesToInt(totolLength);
                        //获取类型常量
                        byte[] type = new byte[4];
                        is.read(type);
                        int iType = NumberBytes.bytesToInt(type);
                        //获取包体
                        byte[] body = new byte[length - totolLength.length - type.length];
                        is.read(body);
                        String clientMsg = new String(body, "UTF-8");

                        Message message = new Message();
                        message.what = RECEIVE_MSG;
                        Bundle bundle = new Bundle();
                        bundle.putInt(RECEIVE_MSG_TYPE_KEY, iType);
                        bundle.putString(RECEIVE_MSG_KEY, clientMsg);
                        message.setData(bundle);
                        mHandler.sendMessage(message);

                        if (iType == ProtocolConst.CLIENT_CLOSE) {
                            isConected = false;
                            socketClients.remove(mSocket);
                            this.is.close();
                            mSocket.close();
                            break;
                        }
                    }
                } catch (IOException e) {
                    Message msg = new Message();
                    msg.what = ERROR_MSG_RECEIVE;
                    Bundle bundle = new Bundle();
                    bundle.putString(ERROR_MSG_KEY, e.getMessage());
                    msg.setData(bundle);
                    mHandler.sendMessage(msg);
                }
            }
        }
    }

    /**
     * 将带有操作类型和信息的内容转化成字节流发送给客户端
     *
     * @param type {@link com.wanhe.protocollibrary.ProtocolConst}
     * @author 陈德基
     * @time 2017/3/15 9:23
     */
    @Keep
    public void sendMsg(int type, String msg) {
        // TODO: 2017/3/14 将对象转换成字符串，再转化成字节数组，并在字节数组前面四位数字节中写入type类型
        try {
            if (TextUtils.isEmpty(msg)) {
                msg = "";
            }
            byte[] bytes = msg.getBytes("UTF-8");
            // TODO: 2017/3/14 前四位写入长度
            byte[] typeByte = NumberBytes.intToBytes(type);
            //写入协议类型常量
            byte[] resultBytes = new byte[4 + typeByte.length + bytes.length];
            byte[] length = NumberBytes.intToBytes(resultBytes.length);
            System.arraycopy(length, 0, resultBytes, 0, length.length);
            System.arraycopy(typeByte, 0, resultBytes, length.length, typeByte.length);
            System.arraycopy(bytes, 0, resultBytes, length.length + typeByte.length, bytes.length);

            //找到现有连接的所有socket客户端，然后发送字节数据
            send2AllSocketClient(resultBytes);
        } catch (IOException e) {
            if (msgChangeListener != null) {
                msgChangeListener.error("发送信息失败：" + e.getMessage());
            }
        }
    }

    /**
     * 发送字节数据给所有连接的客户端
     *
     * @author 陈德基
     * @time 2017/3/15 9:33
     */
    private void send2AllSocketClient(byte[] msg) throws IOException {
        if (socketClients != null && socketClients.size() > 0) {
            for (int i = 0; i < socketClients.size(); i++) {
                OutputStream os = socketClients.get(i).getOutputStream();
                os.write(msg);
                os.flush();
            }
        }
    }

    /**
     * socket服务端资源回收
     *
     * @author 陈德基
     * @time 2017/3/14 10:35
     */
    @Keep
    public void offLine() {
        if (stateListener != null) {
            stateListener.offLine();
        }
        this.isServerOnline = false;
        if (serverSocket != null) {
            try {
                serverSocket.close();
                if (serverThread != null) {
                    serverThread.shutdownNow();
                    serverThread = null;
                }
            } catch (IOException e) {
                if (stateListener != null) {
                    stateListener.error(ERROR_STATE_OFFLINE);
                }
            }
        }
    }

    @Keep
    public interface OnServerStateListener {
        void online();

        void offLine();

        void error(int errorState);
    }

    private OnServerStateListener stateListener;

    @Keep
    public void addServerStateListener(OnServerStateListener stateListener) {
        this.stateListener = stateListener;
    }

    @Keep
    public interface OnMsgChangeListener {
        void receiveClientMsg(int type, String msg);

        void error(String error);
    }

    private OnMsgChangeListener msgChangeListener;

    @Keep
    public void setMsgChangeListener(OnMsgChangeListener msgChangeListener) {
        this.msgChangeListener = msgChangeListener;
    }

    private Handler mHandler;

}
